import Axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios';
import type { PureHttpError, PureHttpRequestConfig, PureHttpResponse, RequestMethods } from './types';
import NProgress from '@/utils/progress';
import { removeUserInfo } from '@/utils/auth';
import { useUserStoreHook } from '@/store/system/user';
import { message } from '@/utils/message';
import { router } from '@/store/utils';
import { defaultConfig, whiteList } from '@/api/service/config';

class PureHttp {
	/** 初始化配置对象 */
	private static initConfig: PureHttpRequestConfig = {};
	/** 保存当前`Axios`实例对象 */
	private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);

	constructor() {
		this.httpInterceptorsRequest();
		this.httpInterceptorsResponse();
	}

	/** 通用请求工具函数 */
	public request<T>(method: RequestMethods, url: string, param?: AxiosRequestConfig, axiosConfig?: PureHttpRequestConfig): Promise<T> {
		const config = { method, url, ...param, ...axiosConfig } as PureHttpRequestConfig;

		// 单独处理自定义请求/响应回调
		return new Promise((resolve, reject) => {
			PureHttp.axiosInstance
				.request(config)
				.then((response: undefined) => {
					resolve(response);
				})
				.catch(error => {
					reject(error);
				});
		});
	}

	/** 文件下载请求 */
	public requestFile(method: RequestMethods, url: string, param?: AxiosRequestConfig, axiosConfig?: PureHttpRequestConfig): Promise<void> {
		const config = { 
			method, 
			url, 
			...param, 
			...axiosConfig,
			responseType: 'blob'
		} as PureHttpRequestConfig;

		return new Promise((resolve, reject) => {
			PureHttp.axiosInstance
				.request(config)
				.then(response => {
					// 获取文件名
					const disposition = response.headers['content-disposition'];
					let filename = 'download';
					if (disposition) {
						const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
						const matches = filenameRegex.exec(disposition);
						if (matches != null && matches[1]) {
							filename = matches[1].replace(/['"]/g, '');
						}
					}

					// 创建下载链接
					const blob = new Blob([response.data]);
					const downloadUrl = window.URL.createObjectURL(blob);
					const link = document.createElement('a');
					link.href = downloadUrl;
					link.download = decodeURIComponent(filename);
					document.body.appendChild(link);
					link.click();
					document.body.removeChild(link);
					window.URL.revokeObjectURL(downloadUrl);
					resolve();
				})
				.catch(error => {
					reject(error);
				});
		});
	}

	/** 单独抽离的`post`工具函数 */
	public post<T, P>(url: string, params?: AxiosRequestConfig<P>, config?: PureHttpRequestConfig): Promise<T> {
		return this.request<T>('post', url, params, config);
	}

	/** 单独抽离的`get`工具函数 */
	public get<T, P>(url: string, params?: AxiosRequestConfig<P>, config?: PureHttpRequestConfig): Promise<T> {
		return this.request<T>('get', url, params, config);
	}

	/** 请求拦截 */
	private httpInterceptorsRequest(): void {
		PureHttp.axiosInstance.interceptors.request.use(
			async (config: PureHttpRequestConfig): Promise<any> => {
				// 开启进度条动画
				NProgress.start();
				// 优先判断post/get等方法是否传入回调，否则执行初始化设置等回调
				if (typeof config.beforeRequestCallback === 'function') {
					config.beforeRequestCallback(config);
					return config;
				}
				if (PureHttp.initConfig.beforeRequestCallback) {
					PureHttp.initConfig.beforeRequestCallback(config);
					return config;
				}
				return config;
			},
			error => error,
		);
	}

	/** 响应拦截 */
  // todo 完善相应的拦截，自动跳转登陆页面
	private httpInterceptorsResponse(): void {
		const instance = PureHttp.axiosInstance;
		instance.interceptors.response.use(
			async (response: PureHttpResponse) => {
				const $config = response.config;
				// 关闭进度条动画
				NProgress.done();
				
				// 如果是文件下载，直接返回response
				if ($config.responseType === 'blob') {
					return response;
				}

				const data = response.data;
				// 登录过期，和异常处理
				if (data.code === 208) {
					message(data.message, { type: 'warning' });
					removeUserInfo();
					await router.push('/login');
				} else if (data.code != 200) {
					message(data.message, { type: 'error' });
				}

				// 优先判断post/get等方法是否传入回调，否则执行初始化设置等回调
				if (typeof $config.beforeResponseCallback === 'function') {
					$config.beforeResponseCallback(response);
					return data;
				}

				if (PureHttp.initConfig.beforeResponseCallback) {
					PureHttp.initConfig.beforeResponseCallback(response);
					return data;
				}

				return data;
			},
			(error: PureHttpError) => {
				error.isCancelRequest = Axios.isCancel(error);

				// 关闭进度条动画
				NProgress.done();
				message(error.message, { type: 'error' });
				return error;
			},
		);
	}
}

export const http = new PureHttp();
